
package com.lantian.common.utils.file;

import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * MinIO对象存储工具类
 * <p>
 * 提供MinIO客户端的初始化和常用操作，包括：
 * 1. 桶操作：创建桶、检查桶是否存在、删除桶、获取所有桶列表
 * 2. 文件操作：上传文件、下载文件、删除文件、获取文件信息、获取文件URL
 * 3. 列表操作：列出桶内所有对象、列出桶内前缀对象
 *
 * @author mengxiangchu
 * @date 2023-09-15
 */
public class MinioUtils {

    /**
     * MinIO客户端实例
     */
    private static MinioClient minioClient;

    /**
     * 初始化MinIO客户端
     *
     * @param endpoint MinIO服务地址
     * @param accessKey 访问密钥
     * @param secretKey 密钥
     * @param secure 是否使用HTTPS
     */
    public static void initClient(String endpoint, String accessKey, String secretKey, boolean secure) {
        minioClient = MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }

    /**
     * 创建桶
     *
     * @param bucketName 桶名称
     * @throws Exception 创建桶时发生的异常
     */
    public static void createBucket(String bucketName) throws Exception {
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 检查桶是否存在
     *
     * @param bucketName 桶名称
     * @return 如果桶存在返回true，否则返回false
     * @throws Exception 检查过程中发生的异常
     */
    public static boolean bucketExists(String bucketName) throws Exception {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 获取所有桶列表
     *
     * @return 桶列表
     * @throws Exception 获取过程中发生的异常
     */
    public static List<Bucket> getAllBuckets() throws Exception {
        return minioClient.listBuckets();
    }

    /**
     * 删除桶
     *
     * @param bucketName 桶名称
     * @throws Exception 删除过程中发生的异常
     */
    public static void removeBucket(String bucketName) throws Exception {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 上传文件（MultipartFile）
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称（文件路径+文件名）
     * @param file 要上传的文件
     * @return 上传结果
     * @throws Exception 上传过程中发生的异常
     */
    public static boolean uploadFile(String bucketName, String objectName, MultipartFile file) throws Exception {
        // 检查桶是否存在，如果不存在则创建
        createBucket(bucketName);

        // 上传文件
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build()
        );

        // 验证文件是否上传成功
        return minioClient.statObject(
                StatObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()
        ) != null;
    }

    /**
     * 上传文件（File）
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称（文件路径+文件名）
     * @param file 要上传的文件
     * @return 上传结果
     * @throws Exception 上传过程中发生的异常
     */
    public static boolean uploadFile(String bucketName, String objectName, File file) throws Exception {
        // 检查桶是否存在，如果不存在则创建
        createBucket(bucketName);

        // 获取文件的MIME类型
        String contentType = MimeTypeUtils.getMimeType(file.getName());
        // 上传文件
        minioClient.uploadObject(
                UploadObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .filename(file.getAbsolutePath())
                        .contentType(contentType)
                        .build()
        );

        // 验证文件是否上传成功
        return minioClient.statObject(
                StatObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()
        ) != null;
    }

    /**
     * 下载文件
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称（文件路径+文件名）
     * @param outputStream 输出流
     * @throws Exception 下载过程中发生的异常
     */
    public static void downloadFile(String bucketName, String objectName, OutputStream outputStream) throws Exception {
        // 获取对象的输入流
        GetObjectResponse response = minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()
        );

        // 将输入流数据写入输出流
        byte[] buf = new byte[1024];
        int bytesRead;
        while ((bytesRead = response.read(buf, 0, buf.length)) >= 0) {
            outputStream.write(buf, 0, bytesRead);
        }

        // 关闭输入流
        response.close();
        // 刷新输出流
        outputStream.flush();
    }

    /**
     * 获取文件URL（临时访问链接）
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称（文件路径+文件名）
     * @param expires 过期时间（秒）
     * @return 文件访问URL
     * @throws Exception 获取URL过程中发生的异常
     */
    public static String getPresignedObjectUrl(String bucketName, String objectName, int expires) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(expires, TimeUnit.SECONDS)
                        .build()
        );
    }

    /**
     * 删除文件
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称（文件路径+文件名）
     * @throws Exception 删除过程中发生的异常
     */
    public static void deleteFile(String bucketName, String objectName) throws Exception {
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()
        );
    }

    /**
     * 批量删除文件
     *
     * @param bucketName 桶名称
     * @param objectNames 文件路径列表
     * @throws Exception 删除过程中发生的异常
     */
    public static void deleteFiles(String bucketName, List<String> objectNames) throws Exception {
        List<DeleteObject> deleteObjects = new ArrayList<>();
        for (String objectName : objectNames) {
            deleteObjects.add(new DeleteObject(objectName));
        }
        minioClient.removeObjects(
                RemoveObjectsArgs.builder()
                        .bucket(bucketName)
                        .objects(deleteObjects)
                        .build()
        );
    }

    /**
     * 获取文件信息
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称（文件路径+文件名）
     * @return 文件信息对象
     * @throws Exception 获取过程中发生的异常
     */
    public static StatObjectResponse getFileInfo(String bucketName, String objectName) throws Exception {
        return minioClient.statObject(
                StatObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()
        );
    }

    /**
     * 列出桶内所有对象
     *
     * @param bucketName 桶名称
     * @return 对象列表
     * @throws Exception 列出过程中发生的异常
     */
    public static List<String> listObjects(String bucketName) throws Exception {
        List<String> objectList = new ArrayList<>();
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .recursive(true)
                        .build()
        );

        for (Result<Item> result : results) {
            Item item = result.get();
            objectList.add(item.objectName());
        }

        return objectList;
    }

    /**
     * 列出桶内前缀对象
     *
     * @param bucketName 桶名称
     * @param prefix 前缀（文件夹路径）
     * @return 对象列表
     * @throws Exception 列出过程中发生的异常
     */
    public static List<String> listObjects(String bucketName, String prefix) throws Exception {
        List<String> objectList = new ArrayList<>();
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(prefix)
                        .recursive(true)
                        .build()
        );

        for (Result<Item> result : results) {
            Item item = result.get();
            objectList.add(item.objectName());
        }

        return objectList;
    }

    /**
     * 获取MinIO客户端实例
     *
     * @return MinIO客户端实例
     */
    public static MinioClient getMinioClient() {
        return minioClient;
    }
}
